开发流程
1:用户在商户APP中选择商品,提交订单,选择微信支付。
2:商户后台收到用户支付单,调用微信支付统一下单接口。参见【统一下单API】。
3:统一下单接口返回正常的prepay_id,再按签名规范重新生成签名后,将数据传输给APP。参与签名的字段名为appid,partnerid,prepayid,noncestr,timestamp,package。注意:package的值格式为Sign=WXPay
4:商户APP调起微信支付。api参见本章节【app端开发步骤说明】
5:商户后台接收支付通知。api参见【支付结果通知API】
6:商户后台查询支付结果。,api参见【查询订单API】
开发中
首先呢我们需要拿到三个参数(appid,mch_id,key),这三个参数分别对应的是 在微信开发平台中创建的移动应用appid,微信支付商户号,商户支付秘钥,详情看参考【支付结果通知API】
然后我们先把统一下单所需要的参数列出来
$request_data = array('appid' => C('WX_APPID'), #应用APPID'mch_id' => C('WX_MCHID'), #商户号'trade_type' => 'APP', #支付类型'nonce_str' => \Org\Util\String::randString(30), #随机字符串 不长于32位'body' => '商品名称', #商品名称'out_trade_no' => '12345678912456', #商户后台订单号'total_fee' => '1', #商品价格'spbill_create_ip' => get_client_ip(), #用户端实际ip'notify_url' => 'http://***/app/index.php/Home/Wxpay/wx_notify', #异步通知回调地址);
这些都是请求参数必填项,其他参数请查看文档
下来我们就要使用这些参数生成签名了
$request_data['sign'] = $this -> get_sign($request_data);
我们下来需要把微信请求的数据拼装成 xml格式,注意:xml数据要使用<![CDATA[]]>包括
$xml_data = $this -> set_xmldata($request_data);打印$xml_data结果如下<xml><appid><![CDATA[wx7ad3cc6c6111111]]></appid><mch_id><![CDATA[1494741111]]></mch_id><trade_type><![CDATA[APP]]></trade_type><nonce_str><![CDATA[WXXWkMDOgLIqhUnITfNrBbJEVGQdRO]]></nonce_str><body><![CDATA[u5546u54c1u540du79f0]]></body><out_trade_no><![CDATA[12345678912456]]></out_trade_no><total_fee><![CDATA[1]]></total_fee><spbill_create_ip><![CDATA[1.86.242.193]]></spbill_create_ip><notify_url><![CDATA[http://***/app/index.php/Home/Wxpay/wx_notify]]></notify_url><sign><![CDATA[EC0BFB3434A72F20C2CA3378BF07264C]]></sign></xml>
现在就可以向微信发送请求了
$res = $this -> send_prePaycurl($xml_data);这是请求的返回值{return_code: "SUCCESS", #业务结果 只有这里返回SUCCESS才会有prepay_idreturn_msg: "OK", #返回结果描述appid: "wx7ad3cc6c6111111", #应用APPIDmch_id: "1494741111", #商户号nonce_str: "jkh9mmRlmSHBJxO0", #随机字符串sign: "AF3B26B1E58591D6565E61DDFBB7837B", #签名result_code: "SUCCESS", #也是业务结果prepay_id: "wx20171226005556c5c65b325a0132782836", #预支付交易会话标识,用于APP请求微信支付调用,有效期两小时trade_type: "APP" #支付类型}
到这里拿到prepay_id还没完我们还需要对返回的数据进行二次签名
if($res['return_code'] == 'SUCCESS' && $res['result_code'] == 'SUCCESS'){$two_data['appid'] = C('WX_APPID'); #APPID$two_data['partnerid'] = C('WX_MCHID'); #商户号$two_data['prepayid'] = $res['prepay_id']; //预支付交易会话标识$two_data['noncestr'] = \Org\Util\String::randString(30);$two_data['timestamp'] = time(); #时间戳$two_data['package'] = "Sign=WXPay"; #固定值$two_data['sign'] = $this -> get_twosign($two_data); #二次签名$this->ajaxReturn(array('code'=>200,'info'=>$two_data));}else{$this->ajaxReturn(array('code'=>201,'info'=>$res['err_code_des']));}
然后就可以在商户APP端通过prepayid进行支付了
下面我们来列出上面调用的几个公共方法
//一次签名的函数private function get_sign($data){ksort($data);$str = '';foreach ($data as $key => $value) {$str .= !$str ? $key . '=' . $value : '&' . $key . '=' . $value;}$str.='&key='.C('WX_KEY');$sign = strtoupper(md5($str));return $sign;}//二次签名的函数private function get_twosign($data){$sign_data = array("appid"=>$data['appid'],"partnerid"=>$data['partnerid'],"prepayid"=>$data['prepayid'],"noncestr"=>$data['noncestr'],"timestamp"=>$data['timestamp'],"package"=>$data['package'],);return $this -> get_sign($sign_data);}//生成xml格式的函数private function set_xmldata($data) {$xmlData = "<xml>";foreach ($data as $key => $value) {$xmlData.="<".$key."><![CDATA[".$value."]]></".$key.">";}$xmlData = $xmlData."</xml>";return $xmlData;}//通过curl发送数据给微信接口的函数private function send_prePaycurl($xmlData) {$url = "https://api.mch.weixin.qq.com/pay/unifiedorder";$header[] = "Content-type: text/xml";$curl = curl_init();curl_setopt($curl, CURLOPT_HTTPHEADER, $header);curl_setopt($curl, CURLOPT_URL, $url);curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);curl_setopt($curl, CURLOPT_POST, 1);curl_setopt($curl, CURLOPT_POSTFIELDS, $xmlData);$data = curl_exec($curl);if (curl_errno($curl)) {print curl_error($curl);}curl_close($curl);return $this -> _xmldataparse($data);}//xml数据解析函数private function _xmldataparse($data){$msg = array();$msg = (array)simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);return $msg;}
微信回调
支付有了,肯定还得有回调
//微信回调public function wx_notify(){//允许从外部加载XML实体(防止XML注入攻击)libxml_disable_entity_loader(true);$postStr = $this -> post_data(); #接收微信返回数据xml格式$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA); #xml格式数据转换成对象$arr = $this -> object_toarray($postObj); #对象转成数组ksort($arr); # 对数据进行排序$str = $this -> params_tourl($arr); #对数据拼接成字符串$user_sign = strtoupper(md5($str)); //把微信返回的数据进行再次签名//验证签名if($user_sign == $arr['sign']){//验证签名成功 处理商户订单逻辑//给微信返回接收到数据通知return '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';}else{//签名验证失败 微信会再次访问回调方法return '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';}}
回调用到的方法如下
// 接收post数据/** 微信是用$GLOBALS['HTTP_RAW_POST_DATA'];这个函数接收post数据的*/public function post_data(){$receipt = $_REQUEST;if($receipt==null){$receipt = file_get_contents("php://input");if($receipt == null){$receipt = $GLOBALS['HTTP_RAW_POST_DATA'];}}return $receipt;}//把对象转成数组public function object_toarray($arr) {if(is_object($arr)) {$arr = (array)$arr;} if(is_array($arr)) {foreach($arr as $key=>$value) {$arr[$key] = $this->object_toarray($value);}}return $arr;}/*** 格式化参数格式化成url参数*/private function params_tourl($arr){$weipay_key = C('WX_KEY');//微信的key,这个是微信支付给你的key,不要瞎填。$buff = "";foreach ($arr as $k => $v){if($k != "sign" && $v != "" && !is_array($v)){$buff .= $k . "=" . $v . "&";}}$buff = trim($buff, "&");return $buff.'&key='.$weipay_key;}
总结:首先微信支付的流程比较多,
公众号,开放平台,微信商户,配置参数的时候要看仔细,不要后面的坑特别多,因为是第一次写微信支付,可能会存在部分问题,欢迎大家可以在下面留言反馈。
这是最后完成的功能

下面分享一下全部的代码
<?phpnamespace Home\Controller;use Think\Controller;/*** php开发微信app支付接口* @global WX_APPID 开放平台->移动应用appid* @global WX_MCHID 微信支付商户号* @global WX_KEY 商户支付秘钥* @author codehi <admin@codehi.top> 2017-12-23*/class WxpayController extends Controller{/*** 微信支付统一下单 >>> 生成预支付交易单*/public function wx_pay(){$request_data = array('appid' => C('WX_APPID'), #应用APPID'mch_id' => C('WX_MCHID'), #商户号'trade_type' => 'APP', #支付类型'nonce_str' => \Org\Util\String::randString(30), #随机字符串 不长于32位'body' => '商品名称', #商品名称'out_trade_no' => '12345678912456', #商户后台订单号'total_fee' => '1', #商品价格'spbill_create_ip' => get_client_ip(), #用户端实际ip'notify_url' => 'http://***/app/index.php/Home/Wxpay/wx_notify', #异步通知回调地址);// 获取签名$request_data['sign'] = $this -> get_sign($request_data);// 拼装数据$xml_data = $this -> set_xmldata($request_data);// 发送请求$res = $this -> send_prePaycurl($xml_data);$this->ajaxReturn($res);if($res['return_code'] == 'SUCCESS' && $res['result_code'] == 'SUCCESS'){$two_data['appid'] = C('WX_APPID'); #APPID$two_data['partnerid'] = C('WX_MCHID'); #商户号$two_data['prepayid'] = $res['prepay_id']; //预支付交易会话标识$two_data['noncestr'] = \Org\Util\String::randString(30);$two_data['timestamp'] = time();$two_data['package'] = "Sign=WXPay";$two_data['sign'] = $this->get_twosign($two_data);$this->ajaxReturn(array('code'=>200,'info'=>$two_data));}else{$this->ajaxReturn(array('code'=>201,'info'=>$res['err_code_des']));}}//通过curl发送数据给微信接口的函数private function send_prePaycurl($xmlData) {$url = "https://api.mch.weixin.qq.com/pay/unifiedorder";$header[] = "Content-type: text/xml";$curl = curl_init();curl_setopt($curl, CURLOPT_HTTPHEADER, $header);curl_setopt($curl, CURLOPT_URL, $url);curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);curl_setopt($curl, CURLOPT_POST, 1);curl_setopt($curl, CURLOPT_POSTFIELDS, $xmlData);$data = curl_exec($curl);if (curl_errno($curl)) {print curl_error($curl);}curl_close($curl);return $this->_xmldataparse($data);}//xml数据解析函数private function _xmldataparse($data){$msg = array();$msg = (array)simplexml_load_string($data, 'SimpleXMLElement', LIBXML_NOCDATA);return $msg;}//生成xml格式的函数private function set_xmldata($data) {$xmlData = "<xml>";foreach ($data as $key => $value) {$xmlData.="<".$key."><![CDATA[".$value."]]></".$key.">";}$xmlData = $xmlData."</xml>";return $xmlData;}//一次签名的函数private function get_sign($data){ksort($data);$str = '';foreach ($data as $key => $value) {$str .= !$str ? $key . '=' . $value : '&' . $key . '=' . $value;}$str.='&key='.C('WX_KEY');$sign = strtoupper(md5($str));return $sign;}//二次签名的函数private function get_twosign($data){$sign_data = array("appid"=>$data['appid'],"partnerid"=>$data['partnerid'],"prepayid"=>$data['prepayid'],"noncestr"=>$data['noncestr'],"timestamp"=>$data['timestamp'],"package"=>$data['package'],);return $this->get_sign($sign_data);}//微信回调public function wx_notify(){//允许从外部加载XML实体(防止XML注入攻击)libxml_disable_entity_loader(true);$postStr = $this -> post_data();//接收post数据$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);$arr = $this -> object_toarray($postObj);//对象转成数组ksort($arr);// 对数据进行排序$str = $this -> params_tourl($arr);//对数据拼接成字符串$user_sign = strtoupper(md5($str));if($user_sign == $arr['sign']){//验证签名return '<xml><return_code><![CDATA[SUCCESS]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';}else{return '<xml><return_code><![CDATA[FAIL]]></return_code><return_msg><![CDATA[OK]]></return_msg></xml>';}}// 接收post数据/** 微信是用$GLOBALS['HTTP_RAW_POST_DATA'];这个函数接收post数据的*/public function post_data(){$receipt = $_REQUEST;if($receipt==null){$receipt = file_get_contents("php://input");if($receipt == null){$receipt = $GLOBALS['HTTP_RAW_POST_DATA'];}}return $receipt;}//把对象转成数组public function object_toarray($arr) {if(is_object($arr)) {$arr = (array)$arr;} if(is_array($arr)) {foreach($arr as $key=>$value) {$arr[$key] = $this->object_toarray($value);}}return $arr;}/*** 格式化参数格式化成url参数*/private function params_tourl($arr){$weipay_key = C('WX_KEY');//微信的key,这个是微信支付给你的key,不要瞎填。$buff = "";foreach ($arr as $k => $v){if($k != "sign" && $v != "" && !is_array($v)){$buff .= $k . "=" . $v . "&";}}$buff = trim($buff, "&");return $buff.'&key='.$weipay_key;}}
